package cn.csg.iotplatform.module.system.service.permission;

import cn.csg.iotplatform.framework.common.util.common.ToolUtil;
import cn.csg.iotplatform.framework.web.core.util.WebFrameworkUtils;
import cn.csg.iotplatform.module.system.controller.admin.permission.vo.role.*;
import cn.csg.iotplatform.module.system.controller.admin.user.vo.user.UserPageReqVO;
import cn.csg.iotplatform.module.system.controller.admin.user.vo.user.UserRespVO;
import cn.csg.iotplatform.module.system.dal.dataobject.permission.UserRoleDO;
import cn.csg.iotplatform.module.system.dal.dataobject.permission.UserRolesDto;
import cn.csg.iotplatform.module.system.dal.dataobject.permission.UserRolesVo;
import cn.csg.iotplatform.module.system.dal.dataobject.permission.vo.RoleVO;
import cn.csg.iotplatform.module.system.dal.dataobject.user.AdminUserDO;
import cn.csg.iotplatform.module.system.dal.mysql.permission.UserRoleMapper;
import cn.csg.iotplatform.module.system.enums.user.UserIdEnum;
import cn.csg.iotplatform.module.system.service.user.AdminUserService;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.extra.spring.SpringUtil;
import cn.csg.iotplatform.framework.common.enums.CommonStatusEnum;
import cn.csg.iotplatform.framework.common.pojo.PageResult;
import cn.csg.iotplatform.framework.common.util.collection.CollectionUtils;
import cn.csg.iotplatform.framework.common.util.object.BeanUtils;
import cn.csg.iotplatform.module.system.dal.dataobject.permission.RoleDO;
import cn.csg.iotplatform.module.system.dal.mysql.permission.RoleMapper;
import cn.csg.iotplatform.module.system.dal.redis.RedisKeyConstants;
import cn.csg.iotplatform.module.system.enums.permission.DataScopeEnum;
import cn.csg.iotplatform.module.system.enums.permission.RoleCodeEnum;
import cn.csg.iotplatform.module.system.enums.permission.RoleTypeEnum;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.google.common.annotations.VisibleForTesting;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors;

import static cn.csg.iotplatform.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.csg.iotplatform.framework.common.util.collection.CollectionUtils.convertMap;
import static cn.csg.iotplatform.framework.common.util.collection.CollectionUtils.convertSet;
import static cn.csg.iotplatform.module.system.enums.ErrorCodeConstants.*;

/**
 * 角色 Service 实现类
 *
 * @author 芋道源码
 */
@Service
@Slf4j
public class RoleServiceImpl implements RoleService {

    @Resource
    private PermissionService permissionService;
    @Resource
    private AdminUserService userService;

    @Resource
    private RoleMapper roleMapper;
    @Resource
    private UserRoleMapper userRoleMapper;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long createRole(RoleSaveReqVO createReqVO, Integer type) {
        //1.校验角色
        this.validateRoleDuplicate(createReqVO.getName(), null, null);
        //2.插入到数据库
        RoleDO role = BeanUtils.toBean(createReqVO, RoleDO.class);
        role.setType(ObjectUtil.defaultIfNull(type, RoleTypeEnum.CUSTOM.getType()));
        role.setStatus(CommonStatusEnum.ENABLE.getStatus());
        role.setDataScope(DataScopeEnum.ALL.getScope()); // 默认可查看所有数据。原因是，可能一些项目不需要项目权限
        roleMapper.insert(role);
        //3.新增角色关联
        this.updateRoleDataScope(role.getId(), createReqVO.getDataScope(), new HashSet<>());
        // 返回
        return role.getId();
    }

    @Override
    @CacheEvict(value = RedisKeyConstants.ROLE, key = "#updateReqVO.id")
    public void updateRole(RoleSaveReqVO updateReqVO, Long userId) {
        //1.校验是否可以更新
        this.validateRoleForUpdateV2(updateReqVO.getId(), userId);
        //2.校验角色的唯一字段是否重复
        this.validateRoleDuplicate(updateReqVO.getName(), null, updateReqVO.getId());
        //3.修改角色关联
        this.updateRoleDataScope(updateReqVO.getId(), updateReqVO.getDataScope(), new HashSet<>());
        //4.更新到数据库
        RoleDO updateObj = BeanUtils.toBean(updateReqVO, RoleDO.class);
        // 返回
        roleMapper.updateById(updateObj);
    }

    @Override
    @CacheEvict(value = RedisKeyConstants.ROLE, key = "#id")
    public void updateRoleStatus(Long id, Integer status, Long userId) {
        // 校验是否可以更新
        validateRoleForUpdateV2(id, userId);

        // 更新状态
        RoleDO updateObj = new RoleDO().setId(id).setStatus(status);
        roleMapper.updateById(updateObj);
    }

    @Override
    @CacheEvict(value = RedisKeyConstants.ROLE, key = "#id")
    public void updateRoleDataScope(Long id, Integer dataScope, Set<Long> dataScopeDeptIds) {
        // 校验是否可以更新
        validateRoleForUpdate(id);

        // 更新数据范围
        RoleDO updateObject = new RoleDO();
        updateObject.setId(id);
        updateObject.setDataScope(dataScope);
        updateObject.setDataScopeDeptIds(dataScopeDeptIds);
        roleMapper.updateById(updateObject);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @CacheEvict(value = RedisKeyConstants.ROLE, key = "#id")
    public void deleteRole(Long id) {
        //1.校验是否可以更新
        this.validateRoleForUpdate(id);
        this.validateUserRole(id);
        //2.标记删除
        this.roleMapper.deleteById(id);
        //3.删除相关数据
        this.permissionService.processRoleDeleted(id);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteBatchRole(RoleDeleteBatchDto dto) {
        Long loginUserId = WebFrameworkUtils.getLoginUserId();
        if (loginUserId != UserIdEnum.SUPERADMIN.getUserId()){
            List<RoleDO> roleDOS = roleMapper.selectList(new LambdaQueryWrapper<RoleDO>().eq(RoleDO::getCreator, loginUserId).in(RoleDO::getId, dto.getIds()));
            if (dto.getIds().size() != roleDOS.size()){
                throw exception(EXIST_CREATED_BY_NONCURRENT_USER);
            }
        }
        dto.getIds().forEach(this::deleteRole);
    }

    /**
     * 校验角色的唯一字段是否重复
     * <p>
     * 1. 是否存在相同名字的角色
     * 2. 是否存在相同编码的角色
     *
     * @param name 角色名字
     * @param code 角色额编码
     * @param id   角色编号
     */
    @VisibleForTesting
    void validateRoleDuplicate(String name, String code, Long id) {
        // 0. 超级管理员，不允许创建
//        if (RoleCodeEnum.isSuperAdmin(code)) {
//            throw exception(ROLE_ADMIN_CODE_ERROR, code);
//        }
        // 1. 该 name 名字被其它角色所使用
        RoleDO role = roleMapper.selectByName(name);
        if (role != null && !role.getId().equals(id)) {
            throw exception(ROLE_NAME_DUPLICATE, name);
        }
/*        // 2. 是否存在相同编码的角色
        if (!StringUtils.hasText(code)) {
            return;
        }
        // 该 code 编码被其它角色所使用
        role = roleMapper.selectByCode(code);
        if (role != null && !role.getId().equals(id)) {
            throw exception(ROLE_CODE_DUPLICATE, code);
        }*/
    }

    @VisibleForTesting
    void validateUserRole(Long id) {
        if (id == null) {
            return;
        }
        List<UserRoleDO> userRoleDOS=userRoleMapper.selectListByRoleIds(Collections.singleton(id));
        if (userRoleDOS.size()!=0) {
            throw exception(ROLE_HAS_USER);
        }
    }

    /**
     * 校验角色是否可以被更新
     *
     * @param id 角色编号
     */
    @VisibleForTesting
    void validateRoleForUpdate(Long id) {
        RoleDO roleDO = roleMapper.selectById(id);
        if (roleDO == null) {
            throw exception(ROLE_NOT_EXISTS);
        }
        // 内置角色，不允许删除
        if (RoleTypeEnum.SYSTEM.getType().equals(roleDO.getType())) {
            throw exception(ROLE_CAN_NOT_UPDATE_SYSTEM_TYPE_ROLE);
        }
    }

    /**
     * 校验角色是否可以被更新
     *
     * @param id 角色编号
     */
    @VisibleForTesting
    void validateRoleForUpdateV2(Long id, Long userId) {
        RoleDO roleDO = roleMapper.selectById(id);
        if (roleDO == null) {
            throw exception(ROLE_NOT_EXISTS);
        }
        // 内置角色，不允许删除
        if (RoleTypeEnum.SYSTEM.getType().equals(roleDO.getType())) {
            throw exception(ROLE_CAN_NOT_UPDATE_SYSTEM_TYPE_ROLE);
        }
        //非当前用户创建的角色不能修改
        AdminUserDO user = userService.getUser(userId);
        if (user == null){
            throw exception(USER_NOT_EXISTS);
        }

        //userId为1表示为超级管理员用户，无视权限控制
        if(userId != 1){
            LambdaQueryWrapper lambdaQueryWrapper = new LambdaQueryWrapper<UserRoleDO>()
                    .eq(UserRoleDO::getUserId, userId)
                    .eq(UserRoleDO::getRoleId, 1);
            UserRoleDO userRoleDO = userRoleMapper.selectOne(lambdaQueryWrapper);
            if (userRoleDO == null){
                if ((roleDO.getCreator() == null || !roleDO.getCreator().equals(String.valueOf(userId)))){
                    throw exception(USER_NO_PERMISSION_TO_OPERATE_THE_DATA);
                }
            }
        }

    }

    @Override
    public RoleDO getRole(Long id) {
        return roleMapper.selectById(id);
    }

    @Override
    @Cacheable(value = RedisKeyConstants.ROLE, key = "#id",
            unless = "#result == null")
    public RoleDO getRoleFromCache(Long id) {
        return roleMapper.selectById(id);
    }


    @Override
    public List<RoleDO> getRoleListByStatus(Collection<Integer> statuses) {
        return roleMapper.selectListByStatus(statuses);
    }

    @Override
    public List<RoleDO> getRoleList() {
        return roleMapper.selectList();
    }

    @Override
    public List<RoleDO> getRoleList(Collection<Long> ids) {
        if (CollectionUtil.isEmpty(ids)) {
            return Collections.emptyList();
        }
        return roleMapper.selectBatchIds(ids);
    }

    @Override
    public List<RoleDO> getRoleListFromCache(Collection<Long> ids) {
        if (CollectionUtil.isEmpty(ids)) {
            return Collections.emptyList();
        }
        // 这里采用 for 循环从缓存中获取，主要考虑 Spring CacheManager 无法批量操作的问题
        RoleServiceImpl self = getSelf();
        return CollectionUtils.convertList(ids, self::getRoleFromCache);
    }

    @Override
    public PageResult<RoleRespVO> getRolePage(RolePageReqVO reqVO) {
        //1.获取登录用户关联角色，判断是否超级管理员，查询创建人为当前登录人或本单位及上级单位创建的公用角色
        boolean isSuper = isSuperAdministrator(reqVO.getUserId());
        if(isSuper){
            reqVO.setUnitId(null);
        }
        //2.查询角色列表
        List<RoleVO> list = this.roleMapper.queryRolePage(reqVO);
        Long total = this.roleMapper.queryRoleTotal(reqVO);

        List<RoleRespVO> roleRespVOS = BeanUtils.toBean(list, RoleRespVO.class);
        //数据拼接(创建人信息)
        Set<Long> creators = CollectionUtils.convertSet(roleRespVOS, RoleRespVO::getCreator);
        UserPageReqVO userPageReqVO = new UserPageReqVO();
        userPageReqVO.setIds(creators);
        PageResult<UserRespVO> userPage = this.userService.queryUserPage(userPageReqVO);

        Map<Long, UserRespVO> longAdminUserDOMap = convertMap(userPage.getList(), UserRespVO::getId);

        List<RoleRespVO> roleDOS = CollectionUtils.convertList(roleRespVOS, role -> convertRoleCreators(role, longAdminUserDOMap.get(role.getCreator())));

        //3.封装返回结果
        return ToolUtil.setPage(total, roleDOS);
    }

    @Override
    public PageResult<RoleRespV2VO> getRolePageV2(RolePageReqVO reqVO) {
        boolean isSuper = isSuperAdministratorV2(reqVO.getUserId());
        if(isSuper){
            reqVO.setUnitId(null);
        }

        //2.查询角色列表
        List<RoleVO> list = this.roleMapper.queryRolePage(reqVO);
        Long total = this.roleMapper.queryRoleTotal(reqVO);

        List<RoleRespV2VO> roleRespVOS = list.stream().map(v -> {
            RoleRespV2VO roleRespVO = new RoleRespV2VO();
            roleRespVO.setId(v.getId());
            roleRespVO.setName(v.getName());
            roleRespVO.setCode(v.getCode());
            roleRespVO.setSort(v.getSort());
            roleRespVO.setStatus(v.getStatus());
            roleRespVO.setType(v.getType());
            roleRespVO.setRemark(v.getRemark());
            roleRespVO.setDataScope(v.getDataScope());
            roleRespVO.setDataScopeDeptIds(v.getDataScopeDeptIds());
            roleRespVO.setCreator(v.getCreator());
            roleRespVO.setCreateTime(v.getCreateTime());
            roleRespVO.setIsPublic(v.getIsPublic());
            roleRespVO.setCreatorName(v.getCreatorName());
            roleRespVO.setCreatorDept(v.getCreatorDept());
            roleRespVO.setDataScopeDesc(v.getDataScopeDesc());
            roleRespVO.setIsPublicDesc(v.getIsPublicDesc());
            if (isSuper){
                roleRespVO.setHaveModifyPermission(1);
            }else {
                roleRespVO.setHaveModifyPermission(v.getCreator() != null && reqVO.getUserId().equals(v.getCreator()) ? 1 : 0);
            }

            return roleRespVO;
        }).collect(Collectors.toList());

        //3.封装返回结果
        return ToolUtil.setPage(total, roleRespVOS);
    }

    private boolean isSuperAdministrator(Long userId){
        Set<Long> userRoleIds = convertSet(userRoleMapper.selectListByUserId(userId), UserRoleDO::getRoleId);
        List<RoleDO> roleDOList = this.roleMapper.selectListByRoleIds(userRoleIds);
        AtomicBoolean isSuper = new AtomicBoolean(false);
        roleDOList.forEach(roleDO -> {
            if(("超级管理员").equals(roleDO.getName())){
                isSuper.set(true);
            }
        });
        return isSuper.get();
    }

    private boolean isSuperAdministratorV2(Long userId){
        return userId != null && userId == UserIdEnum.SUPERADMIN.getUserId();
    }

    private RoleRespVO convertRoleCreators(RoleRespVO role, UserRespVO respVO) {
        if (respVO != null) {
            role.setCreatorName(respVO.getNickname());
            role.setCreatorDept(respVO.getDeptName());
        }
        return role;
    }

    @Override
    public boolean hasAnySuperAdmin(Collection<Long> ids) {
        if (CollectionUtil.isEmpty(ids)) {
            return false;
        }
        RoleServiceImpl self = getSelf();
        return ids.stream().anyMatch(id -> {
            RoleDO role = self.getRoleFromCache(id);
            return role != null && RoleCodeEnum.isSuperAdmin(role.getCode());
        });
    }

    @Override
    public void validateRoleList(Collection<Long> ids) {
        if (CollUtil.isEmpty(ids)) {
            return;
        }
        // 获得角色信息
        List<RoleDO> roles = roleMapper.selectBatchIds(ids);
        Map<Long, RoleDO> roleMap = convertMap(roles, RoleDO::getId);
        // 校验
        ids.forEach(id -> {
            RoleDO role = roleMap.get(id);
            if (role == null) {
                throw exception(ROLE_NOT_EXISTS);
            }
            if (!CommonStatusEnum.ENABLE.getStatus().equals(role.getStatus())) {
                throw exception(ROLE_IS_DISABLE, role.getName());
            }
        });
    }

    @Override
    public PageResult<UserRolesVo> getUserRoleList(UserRolesDto dto) {
        //Long loginUserId = WebFrameworkUtils.getLoginUserId();
        //dto.setUserId(loginUserId);

        List<UserRolesVo> voList = this.roleMapper.getUserRoleList(dto);
        Long total = this.roleMapper.getUserRoleTotal(dto);

        return ToolUtil.setPage(total, voList);
    }

    @Override
    public void batchImport(List<RoleImportExcelDto> list) {
        List<RoleSaveReqVO> roleSaveReqVOS = this.setRoleSaveReqVO(list);

        Set<Long> roleIds = new HashSet<>();
        roleSaveReqVOS.forEach(v -> {
            Long role = null;
            try { //重复问题,过滤重复数据,只导入唯一数据
                role = this.createRole(v, 2);
                roleIds.add(role);
            } catch (Exception e) {
            }
        });

        log.info("角色导入结果:应导入{}条,实际成功{}条", list.size(), roleIds.size());
        log.info("成功导入的角色ids:{}", JSONObject.toJSON(roleIds));

    }

    private List<RoleSaveReqVO> setRoleSaveReqVO(List<RoleImportExcelDto> list) {
        List<RoleSaveReqVO> resultList = new ArrayList<>();
        list.forEach(v -> {
            RoleSaveReqVO reqVo = new RoleSaveReqVO();

            reqVo.setName(v.getName());
            reqVo.setRemark(v.getRemark());

            switch (v.getIsPublic()) {
                case "是":
                    reqVo.setIsPublic(1);
                    break;
                case "否":
                    reqVo.setIsPublic(0);
                    break;
                default:
                    break;
            }

            switch (v.getDataScope()) {
                case "本单位及下级单位":
                    reqVo.setDataScope(4);
                    break;
                case "仅本单位":
                    reqVo.setDataScope(3);
                    break;
                default:
                    break;
            }

            resultList.add(reqVo);
        });

        return resultList;
    }

    @Override
    public boolean hasSelfAndChildDeptDataScope(Long userId) {
        return roleMapper.selectDataScope4RoleCount(userId) > 0;
    }

    @Override
    public boolean isSuperAdmin() {
        return roleMapper.selectSuperAdminRoleByUserId(WebFrameworkUtils.getLoginUserId()) > 0;
    }

    /**
     * 获得自身的代理对象，解决 AOP 生效问题
     *
     * @return 自己
     */
    private RoleServiceImpl getSelf() {
        return SpringUtil.getBean(getClass());
    }

}
